由于 Javascript
是异步的,因此我们可以通过 setTimeout
和 setInterval
函数来指定特定时间执行代码。
function foo() {}
var id = setTimeout(foo, 1000); // returns a Number > 0
上例中,当 setTimeout
函数被调用时,它会返回一个标志延时的 ID
并计划在大约 1000
毫秒后调用 foo
函数,函数 foo
将只会执行一次。
根据 Javascript
引擎的计时方法,以及 Javascript
单线程的本质,因此其他代码执行时可能会阻塞此线程,我们无法保证 setTimeout
函数内调用的函数会在指定的时间被执行。setTimeout
函数的第一个参数将会在全局作用域内执行,因此参数内的 this
将会指向全局对象。
function Foo() {
this.value = 42;
this.method = function() {
// this refers to the global object
console.log(this.value); // will log undefined
};
setTimeout(this.method, 500);
}
new Foo();
这里要注意一个常犯的错误,setTimeout
函数的第一个参数指的是函数对象本身,不能写成类似 setTimeout(foo(), 1000)
,因为 foo()
是函数返回值,而不是 foo
本身。
setInterval 函数的堆调用
从上文已知,setTimeout
中的回调函数只会执行一次,而 setInterval
函数,正如函数的名字一样,它会每隔指定时间执行一次回调函数。
即使回调函数的执行被阻塞,setInterval
函数依然会继续调用更多的回调函数。当间隔时间设置较小时,将会导致回调函数堆积。
function foo(){
// something that blocks for 1 second
}
setInterval(foo, 1000);
上述代码中,函数 foo
被调用后将被阻塞一秒钟。
处理可能阻塞的代码
最简单且最可控的方式就是在回调函数内部使用 setTimeout
函数。
function foo(){
// something that blocks for 1 second
setTimeout(foo, 1000);
}
foo();
这样不仅封装了 setTimeout
的调用,同时也阻止了可能存在的回调函数堆积。foo
函数现在可以自己控制是否继续或终止。
手动清除定时器
清除定时器可以通过传递指定的 ID
给 clearTimeout
或 clearInterval
函数。
var id = setTimeout(foo, 1000);
clearTimeout(id);
清除所有的定时器
Javascript
中并没有内置的函数方法来清除所有的定时器(timeout
或 interval
),不过我们可以使用一种暴力的方法来清除所有的定时器。
// clear "all" timeouts
for(var i = 1; i < 1000; i++) {
clearTimeout(i);
}
但是很明显,由于指定最大值的限制,还会有定时器没有被清除掉。由于 ID
会随着定时器被调用的增加而增加,因此更好的方法是记录下最大的 ID
并一起清除。
// clear "all" timeouts
var biggestTimeoutId = window.setTimeout(function(){}, 1),
i;
for(i = 1; i <= biggestTimeoutId; i++) {
clearTimeout(i);
}
eval 的隐式使用
setTimeout
和 setInterval
函数的第一个参数也可以接收字符串,但是尽量不要使用这个功能,因为这会在内部调用 eval
函数来执行这段字符串。
function foo() {
// will get called
}
function bar() {
function foo() {
// never gets called
}
setTimeout('foo()', 1000);
}
bar();
由于 eval
函数并没有在上例中被直接调用,因此传递到 setTimeout
函数的字符串将会在全局作用域下被执行,所以不会调用函数 bar
内部的 foo
函数。
建议尽量不要在使用定时器函数时通过字符串形式来传递参数。
function foo(a, b, c) {}
// NEVER use this
setTimeout('foo(1, 2, 3)', 1000)
// Instead use an anonymous function
setTimeout(function() {
foo(a, b, c);
}, 1000)
总结
不要使用字符串作为 setTimeout
或 setInterval
函数的参数,当需要向回调函数中传递参数时,我们可以用匿名函数的,在匿名函数内部执行回调函数。
另外,尽量避免使用 setInterval
函数,从而避免可能导致的回调函数堆积现象。
参考
http://bonsaiden.github.io/JavaScript-Garden/#other.timeouts
后言
终于将整个 Javascript
Garden
都学习了一遍,基本上每一个章节都翻译了一遍,同时加上了自己的一些想法和笔记,大概花了半个多月的时间,感觉这的的确确是个很适合自己的学习方法,有时候忘记某些概念,我立马就能在自己的博客中找到相关的知识并及时回顾,由于出自自己的笔下,所以很快就能回忆起来。希望自己能坚持这个好的习惯,也希望自己的博文能给博友们带来些许的帮助,大家相互学习,共同进步!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。